#include "camera.h"
#include "ui_camera.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <netinet/in.h> //  TCP/IPЭ������ͷ�ļ�
#include <sys/socket.h>

#include <QImage>
#include <QBuffer>
#include <QFile>

int res;
int fd_v4l2;
char rgb[640 * 480 * 3];
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
size_t i, max_len = 0;
FILE *fd3 = 0;
int Save_fd;
int flag_save = 0;
bool butFlag = false;
bool showflag = true;

typedef struct
{
    unsigned char *start;
    size_t length;
} buffer_t;

// BMP 文件头结构体
#pragma pack(push, 1)
struct BITMAPFILEHEADER
{
    uint16_t bfType;      // 文件类型，必须为 0x4D42 ("BM")
    uint32_t bfSize;      // 文件大小
    uint16_t bfReserved1; // 保留字，必须为 0
    uint16_t bfReserved2; // 保留字，必须为 0
    uint32_t bfOffBits;   // 像素数据的偏移量
};
#pragma pack(pop)
#ifndef BI_RGB
#define BI_RGB 0
#endif

// BMP 信息头结构体
struct BITMAPINFOHEADER
{
    uint32_t biSize;         // 本结构体的大小
    int32_t biWidth;         // 图像宽度
    int32_t biHeight;        // 图像高度
    uint16_t biPlanes;       // 颜色平面数，必须为 1
    uint16_t biBitCount;     // 每像素位数
    uint32_t biCompression;  // 压缩类型
    uint32_t biSizeImage;    // 图像大小
    int32_t biXPelsPerMeter; // 水平分辨率
    int32_t biYPelsPerMeter; // 垂直分辨率
    uint32_t biClrUsed;      // 颜色表中的条目数
    uint32_t biClrImportant; // 重要颜色数
};

buffer_t buffer[4];
buffer_t current;

int *plcd;
int *lcd_p;
int lcd_fd;

void *lcd_init()
{
    lcd_fd = ::open("/dev/fb0", O_RDWR);

    if (lcd_fd == -1)
    {
        perror("open lcd_file error\n");
        return MAP_FAILED;
    }
    fd3 = ::fopen("data.txt", "a");
    Save_fd = open("data.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);

    plcd = (int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    return plcd;
}

int uninit_lcd()
{
    ::close(lcd_fd);

    if (munmap(lcd_p, 800 * 480 * 4) == -1)
    {
        return -1;
    }
    return 0;
}

unsigned int sign3 = 0;
// YUYV转RGB
int yuyv2rgb(int y, int u, int v)
{
    unsigned int pixel24 = 0;
    unsigned char *pixel = (unsigned char *)&pixel24;
    int r, g, b;
    static int ruv, guv, buv;

    if (sign3)
    {
        sign3 = 0;
        ruv = 1159 * (v - 128);
        guv = 380 * (u - 128) + 813 * (v - 128);
        buv = 2018 * (u - 128);
    }

    r = (1164 * (y - 16) + ruv) / 1000;
    g = (1164 * (y - 16) - guv) / 1000;
    b = (1164 * (y - 16) + buv) / 1000;

    if (r > 255)
        r = 255;
    if (g > 255)
        g = 255;
    if (b > 255)
        b = 255;
    if (r < 0)
        r = 0;
    if (g < 0)
        g = 0;
    if (b < 0)
        b = 0;

    pixel[0] = r;
    pixel[1] = g;
    pixel[2] = b;

    return pixel24;
}

int yuyv2rgb0(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{
    unsigned int in, out;
    int y0, u, y1, v;
    unsigned int pixel24;
    unsigned char *pixel = (unsigned char *)&pixel24;
    unsigned int size = width * height * 2;

    for (in = 0, out = 0; in < size; in += 4, out += 6)
    {
        y0 = yuv[in + 0];
        u = yuv[in + 1];
        y1 = yuv[in + 2];
        v = yuv[in + 3];

        sign3 = 1;
        pixel24 = yuyv2rgb(y0, u, v);
        rgb[out + 0] = pixel[0];
        rgb[out + 1] = pixel[1];
        rgb[out + 2] = pixel[2];

        pixel24 = yuyv2rgb(y1, u, v);
        rgb[out + 3] = pixel[0];
        rgb[out + 4] = pixel[1];
        rgb[out + 5] = pixel[2];
    }

    return 0;
}

int timerId1;

Camera::Camera(QWidget *parent) : QWidget(parent),
                                  ui(new Ui::Camera)
{
    ui->setupUi(this);
    lcd_init();

    fd_v4l2 = ::open("/dev/video7", O_RDWR); // ����secureCRTȷ���豸
    if (fd_v4l2 == -1)
    {
        perror("open");
        exit(-1);
    }

    // ��ȡ���ܲ���
    struct v4l2_capability cap = {};
    res = ::ioctl(fd_v4l2, VIDIOC_QUERYCAP, &cap);
    if (res == -1)
    {
        perror("ioctl cap");
        exit(-1);
    }

    // ��ȷ������ͷ���ܿ���ʹ��
    if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
    {
        printf("is a capture device!\n");
    }
    else
    {
        printf("is not a capture device!\n");
        exit(-1);
    }

    // ��ȡ����ͷ֧�ֵĸ�ʽ
    struct v4l2_fmtdesc fmt = {};
    fmt.index = 0;
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // ��ȡ����ͷ��ʽ

    while ((res = ::ioctl(fd_v4l2, VIDIOC_ENUM_FMT, &fmt)) == 0)
    {
        printf("pixformat = %c %c %c %c,description = %s\n",
               fmt.pixelformat & 0xff,
               (fmt.pixelformat >> 8) & 0xff,
               (fmt.pixelformat >> 16) & 0xff,
               (fmt.pixelformat >> 24) & 0xff,
               fmt.description);
        fmt.index++;
    }

    // ���òɼ�ͨ��
    int index = 0; // ʹ��ͨ��0
    res = ::ioctl(fd_v4l2, VIDIOC_S_INPUT, &index);
    if (res == -1)
    {
        perror("ioctl_s_input");
        exit(-1);
    }

    // ��������ͷ�ɼ���ʽ
    struct v4l2_format format = {};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = 640;
    format.fmt.pix.height = 480;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // YUYV
    format.fmt.pix.field = V4L2_FIELD_NONE;
    res = ::ioctl(fd_v4l2, VIDIOC_S_FMT, &format);
    if (res == -1)
    {
        perror("ioctl s_fmt");
        exit(-1);
    }

    // ���뻺��ռ�
    struct v4l2_requestbuffers req = {};
    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    res = ::ioctl(fd_v4l2, VIDIOC_REQBUFS, &req);
    if (res == -1)
    {
        perror("ioctl reqbufs");
        exit(-1);
    }

    // ����ӳ�����
    for (i = 0; i < 4; i++)
    {
        struct v4l2_buffer buf = {};
        buf.index = i; // 0~3չ��4֡ͼƬ
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        res = ::ioctl(fd_v4l2, VIDIOC_QUERYBUF, &buf);
        if (res == -1)
        {
            perror("ioctl querybuf");
            exit(-1);
        }

        // �ж�����¼��󳤶ȣ����������ͼ֡
        if (buf.length > max_len)
            max_len = buf.length;

        // ӳ��
        buffer[i].length = buf.length;
        buffer[i].start = reinterpret_cast<unsigned char *>(mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd_v4l2, buf.m.offset));

        if (buffer[i].start == MAP_FAILED)
        {
            perror("mmap");
            exit(-1);
        }

        // ���
        res = ::ioctl(fd_v4l2, VIDIOC_QBUF, &buf);
        if (res == -1)
        {
            perror("ioctl qbuf");
            exit(-1);
        }
    }

    // ������ʱ������
    current.start = static_cast<unsigned char *>(malloc(max_len)); // 使用 unsigned char*

    if (current.start == NULL)
    {
        perror("malloc");
        exit(-1);
    }

    // �������ͷ
    res = ::ioctl(fd_v4l2, VIDIOC_STREAMON, &buf_type);
    if (res == -1)
    {
        perror("ioctl streamon");
        exit(-1);
    }

    // ��ʱ
    ::sleep(1);

    // RGB������
}

Camera::~Camera()
{
    delete ui;
}

void Camera::timerEvent(QTimerEvent *event)
{
    struct v4l2_buffer buf = {};
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    // IOCTL 操作
    res = ::ioctl(fd_v4l2, VIDIOC_DQBUF, &buf);
    if (res == -1)
    {
        perror("ioctl dqbuf");
        return;
    }

    // 将数据复制到 current.start
    memcpy(current.start, buffer[buf.index].start, buf.bytesused);
    current.length = buf.bytesused;

    // IOCTL 操作
    res = ::ioctl(fd_v4l2, VIDIOC_QBUF, &buf);
    if (res == -1)
    {
        perror("ioctl qbuf");
        return;
    }

    // YUYV转RGB
    unsigned char rgb[640 * 480 * 3]; // RGB 输出数组

    // 显示LCD的逻辑

    int x, y;
    yuyv2rgb0(current.start, rgb, 640, 480);
    if (showflag)
    {
        if (!butFlag)
        {
            for (y = 0; y < 480; y++)
            {
                for (x = 0; x < 640; x++)
                {
                    *(plcd + y * 800 + x) = rgb[3 * (y * 640 + x)] << 16 | rgb[3 * (y * 640 + x) + 1] << 8 | rgb[3 * (y * 640 + x) + 2];
                    // if (flag_save == 1)
                    // {
                    // saveImageData(rgb, x, y);
                    // saveAsBmp(rgb, 640, 480, "image.bmp");
                    // flag_save = 0; // 重置标志
                    //}

                    if (imageWriteFlag)
                        saveAsJpeg(rgb, 640, 480, "./image.jpeg");
                    // flag_save = 0; // 重置标志
                    //}
                }
            }
            // flag_save++;
            // if (flag_save == 100)
            //     flag_save = 0;
        }
        else
        {
            for (y = 0; y < 480; y++)
            {
                for (x = 0; x < 640; x++)
                {
                    *(plcd + y * 800 + x) = 0; // 用透明色填充
                }
            }
        }
        if (flag_save == 1)
        {
            saveAsPng(rgb, 640, 480, "./image.png");
            flag_save = 0; // 重置标志
        }
    }
    // flag_save++;
    // if (flag_save == 100)
    //     flag_save = 0;
    // if (flag_save == 1)
    // {
    // saveImageData(rgb, x, y);
    // saveAsBmp(rgb, 640, 480, "image.bmp");
    if (imageWriteFlag)
        saveAsJpeg(rgb, 640, 480, "./image.jpeg");
    // flag_save = 0; // 重置标志
    // }
}

void Camera::saveImageData(unsigned char *rgb, int x, int y)
{
    static QFile file;
    static QTextStream stream;

    if (y == 0 && x == 0) // 当开始新的一行时打开文件
    {
        file.setFileName("/mnt/udisk/data_pic.txt");
        if (!file.open(QIODevice::Append | QIODevice::Text))
            return;
        stream.setDevice(&file);
    }

    if (y == 479 && x == 639) // 当结束一行时关闭文件
    {
        stream.flush();
        file.close();
    }

    stream << rgb[3 * (y * 640 + x)] << "," << rgb[3 * (y * 640 + x) + 1] << "," << rgb[3 * (y * 640 + x) + 2] << "\n";
}

void Camera::on_pushButton_clicked()
{
    emit goback();
    showflag = false;
}

void Camera::on_pushButton_2_clicked()
{

    if (butFlag)
    {

        ui->pushButton_2->setText("关闭摄像头");
        timerId1 = this->startTimer(10);
        butFlag = false;
    }
    else
    {

        this->killTimer(timerId1);
        ui->pushButton_2->setText("打开摄像头");
        butFlag = true;
        // butFlag = true;
    }
}

void Camera::on_pushButton_3_clicked()
{
    if (flag_save)
    {
        ui->pushButton->setText("获取");
        flag_save = 0;
    }
    else
    {
        flag_save = 1;
        ui->pushButton->setText("停止获取");
    }
}

void Camera::saveAsBmp(const unsigned char *rgb, int width, int height, const QString &filename)
{
    // 创建并打开文件
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly))
    {
        return;
    }

    // 准备文件头和信息头
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    fileHeader.bfType = 0x4D42; // "BM"
    fileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + width * height * 3;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    infoHeader.biSize = sizeof(BITMAPINFOHEADER);
    infoHeader.biWidth = width;
    infoHeader.biHeight = height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biCompression = BI_RGB;
    infoHeader.biSizeImage = width * height * 3;
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    // 写入文件头和信息头
    file.write((char *)&fileHeader, sizeof(BITMAPFILEHEADER));
    file.write((char *)&infoHeader, sizeof(BITMAPINFOHEADER));

    // 写入像素数据
    for (int y = height - 1; y >= 0; y--)
    {
        for (int x = 0; x < width; x++)
        {
            int offset = (y * width + x) * 3;
            file.write((char *)(rgb + offset), 3);
        }
    }

    // 关闭文件
    file.close();
}

void Camera::saveAsJpeg(const unsigned char *rgb, int width, int height, const QString &filename)
{

    QImage image(rgb, width, height, QImage::Format_RGB888);
    QByteArray byteArray;

    QBuffer buffer(&byteArray);
    buffer.open(QIODevice::WriteOnly);
    image.save(&buffer, "JPEG", 25); // 这里的 75 是 JPEG 压缩质量，范围是 0-100

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly))
    {
        return;
    }

    file.write(byteArray);
    file.close();
    imageWriteFlag = false;
    imageReadFlag = true;
}
void Camera::saveAsPng(const unsigned char *rgb, int width, int height, const QString &filename)
{
    QImage image(rgb, width, height, QImage::Format_RGB888);
    QByteArray byteArray;

    QBuffer buffer(&byteArray);
    buffer.open(QIODevice::WriteOnly);
    // 保存为PNG格式，默认就是无损压缩
    if (!image.save(&buffer, "PNG", 25))
    {
        qDebug() << "Failed to save image as PNG";
    }

    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly))
    {
        qDebug() << "Failed to open file for writing";
        return;
    }

    file.write(byteArray);
    file.close();
    imageWriteFlag = false;
    imageReadFlag = true;
}